﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

using System.Threading;

namespace T.FAS.Runtime.Base.Common
{
    public static class FasTools
    {
        #region 命令操作
        /// <summary>
        /// 启动Node
        /// </summary>
        /// <param name="workDir"></param>
        /// <param name="args"></param>
        /// <exception cref="Exception"></exception>
        public static void StartNode(string workDir, string args)
        {
            try
            {
                using (Process pro = new Process())
                {
                    pro.StartInfo.FileName = "npm";
                    pro.StartInfo.Arguments = args;
                    if (!string.IsNullOrEmpty(workDir))
                    {
                        pro.StartInfo.WorkingDirectory = workDir;
                    }

                    pro.Start();
                }
            }
            catch
            {
                try
                {
                    using (var pro = new Process())
                    {
                        pro.StartInfo.FileName = "C:\\Program Files\\nodejs\\npm.cmd";
                        pro.StartInfo.Arguments = args;
                        if (!string.IsNullOrEmpty(workDir))
                        {
                            pro.StartInfo.WorkingDirectory = workDir;
                        }
                        pro.Start();
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception($"完整路径启动npm失败：{ex.Message}，工作目录：{workDir}，启动参数：{args}");
                }
            }
        }

        /// <summary>
        /// 执行命令打开窗口
        /// </summary>
        /// <param name="projectPath"></param>
        /// <param name="cmd"></param>
        /// <param name="createNoWindow"></param>
        public static void NpmRun(string projectPath, int port)
        {
            FileTool.DeleteFile(Path.Combine(projectPath, "output.log"));
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                UseShellExecute = false, // 使用操作系统的 shell 启动进程
                CreateNoWindow = true, // 不创建新窗口
                FileName = "cmd.exe",
                Arguments = $"/c npm run dev {port} 0.0.0.0  2>> output.log",
                WorkingDirectory = projectPath,
            };
            Process process = Process.Start(startInfo);
            Thread.Sleep(10000);
            var retryCount = 3;
            while (retryCount > 0)
            {
                if (IsPortOpen("localhost", port))
                {
                    break;
                }
                else
                {
                    if (retryCount == 1)
                        process?.Kill();
                    else
                        Thread.Sleep(10000);
                }
                retryCount--;
            }
        }
        /// <summary>
        /// 执行命令打开窗口
        /// </summary>
        /// <param name="projectPath"></param>
        /// <param name="cmd"></param>
        /// <param name="createNoWindow"></param>
        public static void NpmRun(string projectPath, int port, bool createNoWindow, bool useShellExecute)
        {
            FileTool.DeleteFile(Path.Combine(projectPath, "output.log"));
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                UseShellExecute = useShellExecute, // 使用操作系统的 shell 启动进程
                CreateNoWindow = createNoWindow, // 不创建新窗口
                FileName = "C:\\Program Files\\nodejs\\npm.cmd",
                Arguments = $"npm run dev {port} 0.0.0.0  2>> output.log",
                WorkingDirectory = projectPath,
            };
            Process process = Process.Start(startInfo);
            Thread.Sleep(10000);
            var retryCount = 3;
            while (retryCount > 0)
            {
                if (IsPortOpen("localhost", port))
                {
                    break;
                }
                else
                {
                    if (retryCount == 1)
                        process?.Kill();
                    else
                        Thread.Sleep(10000);
                }
                retryCount--;
            }
        }
        /// <summary>
        /// 执行命令打开窗口
        /// </summary>
        /// <param name="projectPath"></param>
        /// <param name="cmd"></param>
        /// <param name="createNoWindow"></param>
        public static void ExecuteCmd(string projectPath, string cmd)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                UseShellExecute = true, // 使用操作系统的 shell 启动进程
                CreateNoWindow = false, // 不创建新窗口
                FileName = "cmd.exe",
                Arguments = $"/c " + cmd,
                WorkingDirectory = projectPath,
            };
            Process.Start(startInfo);
            Thread.Sleep(5000);
        }
        /// <summary>
        /// 安装Npm
        /// </summary>
        /// <param name="filePath"></param>
        /// <exception cref="Exception"></exception>
        public static void NpmInstall(string filePath)
        {
            var node_modules = Path.Combine(filePath, "node_modules");
            if (!Directory.Exists(node_modules) || (Directory.Exists(node_modules) && Directory.GetDirectories(node_modules).Length <= 1))
                ExecuteCmdNoWindow(filePath, "pnpm install");//安装全部依赖
            if (!Directory.Exists(node_modules))
            {
                throw new Exception($"pnpm install失败，请联系管理员");
            }
        }
        /// <summary>
        /// 执行命令不打开窗口
        /// </summary>
        /// <param name="projectPath"></param>
        /// <param name="cmd"></param>
        public static void ExecuteCmdNoWindow(string projectPath, string cmd)
        {
            if (!Directory.Exists(projectPath))
            {
                Directory.CreateDirectory(projectPath);
            }
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = "cmd.exe";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;// 重定向标准输入流（从程序写入数据到标准输入流）
            pro.StartInfo.RedirectStandardOutput = true;// 重定向标准输出流（从标准输出流读取数据到程序）
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            pro.StartInfo.WorkingDirectory = projectPath;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine(cmd);
            pro.StandardInput.WriteLine("exit");
            string line;
            StringBuilder msg = new StringBuilder();
            while ((line = pro.StandardOutput.ReadLine()) != null)
            {
                msg.AppendLine(line);
            }
            if (msg.ToString().Contains("failed"))
            {
                pro.Close();
                throw new Exception($"编译失败{msg}");
            }
            pro.Close();
        }
        /// <summary>
        /// 根据路径杀掉进程
        /// </summary>
        /// <param name="filePath"></param>
        public static void KillProcessByPath(string filePath)
        {
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = @"C:\Tools\Handle\handle64.exe";
            pro.StartInfo.Arguments = filePath;
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;// 重定向标准输入流（从程序写入数据到标准输入流）
            pro.StartInfo.RedirectStandardOutput = true;// 重定向标准输出流（从标准输出流读取数据到程序）
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine("/accepteula");
            pro.StandardInput.WriteLine("exit");
            string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";

            string outputTool = pro.StandardOutput.ReadToEnd();
            foreach (Match match in Regex.Matches(outputTool, matchPattern))
            {
                int processID = int.Parse(match.Value);
                Process.GetProcessById(processID).Kill();
            }
            pro.Close();
        }
        /// <summary>
        /// 根据端口号杀掉进程
        /// </summary>
        /// <param name="port"></param>
        public static void KillProcessByPort(int port)
        {
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = "cmd.exe";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;
            pro.StartInfo.RedirectStandardOutput = true;
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine($"netstat -ano|findstr {port}");
            pro.StandardInput.WriteLine("exit");
            // 获取结果
            Regex reg = new Regex("\\s+", RegexOptions.Compiled);
            string line;
            while ((line = pro.StandardOutput.ReadLine()) != null)
            {
                line = line.Trim();
                if (!line.StartsWith("TCP", StringComparison.OrdinalIgnoreCase))
                    continue;
                line = reg.Replace(line, ",");
                string[] arr = line.Split(',');
                if (arr[1].EndsWith($":{port}") || arr[1].Contains($":{port}"))
                {
                    int pid = 0;
                    if (int.TryParse(arr[4], out pid))
                    {
                        Process currPro = Process.GetProcessById(pid);
                        currPro.Kill();
                    }
                }
            }
            pro.Close();
        }
        /// <summary>
        /// 校验端口是否被占用
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool CheckPort(int port)
        {
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = "cmd.exe";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;
            pro.StartInfo.RedirectStandardOutput = true;
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine($"netstat -ano|findstr {port}");
            pro.StandardInput.WriteLine("exit");
            // 获取结果
            Regex reg = new Regex("\\s+", RegexOptions.Compiled);
            string line;
            while ((line = pro.StandardOutput.ReadLine()) != null)
            {
                line = line.Trim();
                if (!line.StartsWith("TCP", StringComparison.OrdinalIgnoreCase))
                    continue;
                line = reg.Replace(line, ",");
                string[] arr = line.Split(',');
                if (arr[1].EndsWith($":{port}") || arr[1].Contains($":{port}"))
                {
                    int pid = 0;
                    if (int.TryParse(arr[4], out pid) && pid != 0)
                    {
                        Process currPro = Process.GetProcessById(pid);
                        if (currPro.ProcessName != "node")
                        {
                            pro.Close();
                            throw new Exception($"端口{port}被占用，请联系管理员重新分配端口");
                        }
                        else
                            return true;
                    }
                }
            }
            pro.Close();
            return false;
        }

        /// <summary>
        /// 校验端口是否被占用
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool CheckPortOccupy(int port)
        {
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = "cmd.exe";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;
            pro.StartInfo.RedirectStandardOutput = true;
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine($"netstat -ano|findstr {port}");
            pro.StandardInput.WriteLine("exit");
            // 获取结果
            Regex reg = new Regex("\\s+", RegexOptions.Compiled);
            string line;
            while ((line = pro.StandardOutput.ReadLine()) != null)
            {
                line = line.Trim();
                if (!line.StartsWith("TCP", StringComparison.OrdinalIgnoreCase))
                    continue;
                line = reg.Replace(line, ",");
                string[] arr = line.Split(',');
                if (arr[1].EndsWith($":{port}") || arr[1].Contains($":{port}"))
                {
                    int pid = 0;
                    if (int.TryParse(arr[4], out pid))
                    {
                        Process currPro = Process.GetProcessById(pid);
                        if (currPro != null)
                            return true;
                    }
                }
            }
            pro.Close();
            return false;
        }
        /// <summary>
        /// 校验端口是否启动
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool CheckPortRun(int port)
        {
            Process pro = new Process();
            // 设置命令行、参数
            pro.StartInfo.FileName = "cmd.exe";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;
            pro.StartInfo.RedirectStandardOutput = true;
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            // 启动CMD
            pro.Start();
            // 运行端口检查命令
            pro.StandardInput.WriteLine($"netstat -ano|findstr {port}");
            pro.StandardInput.WriteLine("exit");
            // 获取结果
            Regex reg = new Regex("\\s+", RegexOptions.Compiled);
            string line;
            while ((line = pro.StandardOutput.ReadLine()) != null)
            {
                line = line.Trim();
                if (!line.StartsWith("TCP", StringComparison.OrdinalIgnoreCase))
                    continue;
                line = reg.Replace(line, ",");
                string[] arr = line.Split(',');
                if (arr[1].EndsWith($":{port}") || arr[1].Contains($":{port}"))
                {
                    int pid = 0;
                    if (int.TryParse(arr[4], out pid) && pid != 0)
                    {
                        Process currPro = Process.GetProcessById(pid);
                        if (!currPro.HasExited && currPro.ProcessName == "node")
                            return true;
                    }
                }
            }
            pro.Close();
            return false;
        }
        /// <summary>
        /// 校验端口是否打开
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool IsPortOpen(string host, int port)
        {
            try
            {
                using (TcpClient tcpClient = new TcpClient())
                {
                    tcpClient.Connect(host, port);
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }
        #endregion
        /// <summary>
        /// 获取HashCode
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetHashCode(string str)
        {
            using (MD5 md5Hash = MD5.Create())
            {
                byte[] bytes = Encoding.UTF8.GetBytes(str);
                byte[] hashBytes = md5Hash.ComputeHash(bytes);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            }
        }
    }
}
